#include "Unit_Test.h"
// all trax programs must include trax.hpp
#include "trax.hpp"
#include "Image.h"
#include "Vector3.h"
#include "Sphere.h"
#include "HitRecord.h"
#include "PinHoleCamera.h"
#include "Scene.h"
#include "Shader.h"
#include "AABBox.h"
#include "atTriangle.h"
#include "bvhStack.h"

namespace AtomTrace{
    
void Unit_test_Vector3()
{
	Vector3 vec1(1.0f, 2.0f, 1.0f);
	Vector3 vec2(2.0f, 3.0f, 0.0f);

	Vector3 vec3 = vec1 + vec2;

	printf("vec3_1[%f, %f, %f]\n", vec3[0], vec3[1], vec3[2]);

    //vec3 = Vector3::UNIT_X;
    //for(int i=0; i<1; i++)
	//printf("UNIT_X:[%f, %f, %f]\n", Vector3::UNIT_X[0], Vector3::UNIT_X[1], Vector3::UNIT_X[2]);
}

void Unit_test_BoxIntersection()
{
    //using namespace AtomTrace;
    AABBox box(Vector3(-1.0f, -1.0f, -1.0f), Vector3(1.0f, 1.0f, 1.0f));
    
    int xres = GetXRes();
	int yres = GetYRes();
	int start_fb = GetFrameBuffer();
	Image image(xres, yres, start_fb);

    PinHoleCamera cam;

    cam.position.Set(4.0f, -15.0f, -2.0f);
    cam.dir.Set(-0.255551f, 0.958315, 0.127775);
    //cam.lookat = cam.position + gaze;
    cam.up.Set(0.0f, 0.0f, 1.0f);

    cam.fovy = 0.194f; // tan(22 degree)
    cam.imgWidth = xres;
    cam.imgHeight = yres;
    
    cam.UpdateCamParameter();

    int imageSize = xres * yres;
    Color red, black, res;
    red.Set(1.0f, 0.0f, 0.0f);
    black.Set(0.0f, 0.0f, 0.0f);

    Vector3 des;

    for(int pix = 0; pix < imageSize; pix++)
    {
        Ray ray = cam.CastRay(pix);
        HitRecord hit;
        hit.Init();
        bool isHit = false;
        //for(int objI = 0; objI < scene.numSpheres; objI++)
        //int objI = 2;
        {
            isHit |= box.Intersect(ray, hit.t);
        }

        if(isHit)
        {
            des = ray._o + ray._dir * hit.t;
            for(int i=0; i<3; i++)
            {
                des[i] = des[i] * 0.5f + 0.5f;
            }
            res.Set(des[0], des[1], des[2]);
            res.Clamp();
            image.set(pix, res);
            //printf("hit.normal[%f, %f, %f]\n", hit.N[0], hit.N[1], hit.N[2]);
            //printf("hit.P[%f, %f, %f]\n", hit.P[0], hit.P[1], hit.N[2]);
            //printf("[%i, %i] isHit\n");
        }else
        {
            image.set(pix, black);
        }

    }
}

void Unit_test_TriangleIntersection()
{
    atTriangle tri1(Vector3(-1.0f, -0.5f, 0.0f),
                    Vector3( 1.0f, -0.5f, 0.0f),
                    Vector3( 0.0f,  1.0f, 0.0f));

    int xres = GetXRes();
	int yres = GetYRes();
	int start_fb = GetFrameBuffer();
	Image image(xres, yres, start_fb);

    PinHoleCamera cam;

    cam.position.Set(0.0f, 0.0f, 10.0f);
    cam.dir.Set(0.0f, 0.0f, -1.0f);
    //cam.lookat = cam.position + gaze;
    cam.up.Set(0.0f, 1.0f, 0.0f);

    cam.fovy = 0.194f; // tan(22 degree)
    cam.imgWidth = xres;
    cam.imgHeight = yres;
    
    cam.UpdateCamParameter();

    int imageSize = xres * yres;
    Color red, black, res;
    red.Set(1.0f, 0.0f, 0.0f);
    black.Set(0.0f, 0.0f, 0.0f);
    res = black;
    PointLight light;
    light.pos.Set(1.0f, 0.0f, 1.0f);

    Vector3 des;

    for(int pix = 0; pix < imageSize; pix++)
    {
        Ray ray = cam.CastRay(pix);
        HitRecord hit;
        hit.Init();
        bool isHit = false;
        //for(int objI = 0; objI < scene.numSpheres; objI++)
        //int objI = 2;
        {
            isHit |= tri1.Intersect(ray, hit);
        }

        if(isHit)
        {
            des = hit.P;//ray._o + ray._dir * hit.t;
            for(int i=0; i<3; i++)
            {
                des[i] = des[i] * 0.5f + 0.5f;
            }
            res.Set(des[0], des[1], des[2]);
            /**/
            //res = red * hit.N.Dot(light.GetDirection(hit.P).GetNormalize());
            res.Clamp();
            image.set(pix, res);
            //printf("hit.normal[%f, %f, %f]\n", hit.N[0], hit.N[1], hit.N[2]);
            //printf("hit.P[%f, %f, %f]\n", hit.P[0], hit.P[1], hit.N[2]);
            //printf("[%i, %i] isHit\n");
        }else
        {
            image.set(pix, black);
        }

    }

}

void Unit_test_BVH_Stack()
{
    bvhStack stack;
    const int listSize = 7;
    int testList[listSize] = {6, 8, 90, 34, 342, 32, 9823};

    printf("Test list:\n");
    for(int i=0; i<listSize; i++)
    {
        printf("%d, ", testList[i]);
        stack.Push(testList[i]);
    }
    printf("\n");

    printf("Stack become:\n");
    for(int i=0; i<stack.GetStackSize(); i++)
    {
        printf("%d, ", stack[i]);
    }
    printf("\n");
    printf("Stack current: %d\n", stack.GetCurrent());

    printf("Poping stack:\n");
    while(!stack.IsEmpty())
    {
        printf("Pop %d.\n", stack.Pop());
        //for(int i=0; i<=stack.GetCurrent(); i++)
        for(int i=0; i<stack.GetStackSize(); i++)
        {
            printf("%d, ", stack[i]);
        }
        printf("current=%d\n", stack.GetCurrent());
    }

    printf("Finish BVH stack unit test.\n");
}

} // namespace AtomTrace
